feat(broadcast): add a bounded MPMC broadcast channel - #253
Conversation
Benchmark results40 runs per configuration; the ratio is taken within each run so drift cancels, then averaged.
Not bounded-specific. Same method on the existing unbounded comparison — same direction, and ahead of
So the cost sits in the retention machinery #117 shipped and #213 asks this PR to reuse: one A rejected optimisation.
Net-neutral at best, so Closing the two bad regimes means revisiting the |
Add `broadcast::mpmc::bounded`, a lossless bounded broadcast channel. Every accepted value stays readable by every subscription that was active when it was accepted, so a receive never reports lag. Capacity counts the shared backlog held by the slowest active subscription: `send` waits on it and `try_send` reports `TrySendError::Full` without taking the value. Nothing slides, overwrites, or is dropped to make room. Move the backlog, the cursors, and the one receive poll step both families share into a private `common` module, while each keeps its own publish path and `Recv` future so neither retention contract hides behind a shared abstraction. Ring, cursor, retention, and sequencing types stay private to `broadcast::mpmc`. Signed-off-by: onenewcode <lovestudy@qq.com>
1d2bbb7 to
4ffb990
Compare
| finished_rx | ||
| .recv_timeout(Duration::from_secs(10)) | ||
| .expect("reclaimed message destructors must not run while the channel is locked"); |
|
Thanks for your contribution @onenewcode and thanks for @ariesdevil's review. Merging ... |
Summary
Closes #213.
broadcast::mpmc::bounded, a lossless broadcast channel whose shared backlog applies backpressure at the requested capacity.async-broadcast; measure bulk permit release and subscriber reclamation separately from setup and cleanup.Validation:
cargo +1.86.0 x test(586 passing tests),cargo x check,cargo x lint,cargo x bench --no-run, all 100 bounded ecosystem benchmark smoke cases, and six targeted Miri tests covering semaphore batches, panic/overflow recovery, and payload destruction outside locks.Design Notes
Each accepted publication has one commit order and stays readable by subscriptions active at commit time until they advance or are dropped. New subscriptions start at the committed tail. With no subscriptions, sends succeed and discard the value. Zero capacity is rejected.
Capacity counts unread messages in the shared backlog, not application work or pending sends. Receiving advances the subscription before returning the value; there is no acknowledgement or processing-completion barrier. Senders use semaphore notifications as retry hints, so capacity is not reserved and publication is not FIFO among waiting producers.
Payload cloning and destruction run outside the channel lock. Reclaimed capacity is released before touching payloads, and cancelled send registrations are released before their payloads are destroyed.